package org.modbusPorject.util;

import com.intelligt.modbus.jlibmodbus.Modbus;
import com.intelligt.modbus.jlibmodbus.data.ModbusHoldingRegisters;
import com.intelligt.modbus.jlibmodbus.exception.IllegalDataAddressException;
import com.intelligt.modbus.jlibmodbus.exception.IllegalDataValueException;
import com.intelligt.modbus.jlibmodbus.exception.ModbusIOException;
import com.intelligt.modbus.jlibmodbus.slave.ModbusSlave;
import com.intelligt.modbus.jlibmodbus.slave.ModbusSlaveFactory;
import com.intelligt.modbus.jlibmodbus.tcp.TcpParameters;
import org.modbusPorject.holder.MyOwnDataHolder;
import org.modbusPorject.service.ModbusEventListener;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.List;

/**
 * @BelongsProject: study
 * @BelongsPackage: org.modbusPorject.util
 * @Author: zhangzhanheng
 * @CreateTime: 2022-09-26  10:05
 * @Description: ModBus Util
 * @Version: 1.0
 */
@Component
public class ModbusSlaveUtil implements CommandLineRunner {

    @Override
    public void run(String... args) throws Exception {
        Thread.sleep(10000);
        new Thread(new Runnable() {

            @Override
            public void run() {
                createSalve();
            }
        }).start();

    }

    private static int[] arr = new int[]{0,0};

    public static ModbusSlave slave ;

    public void createSalve(){
        try{
            initSlave();
            System.out.println("slave start...........");
            createDataHolder();
            closeSlave();
        }catch (Exception e){

        }
    }

    public static void initSlave() throws UnknownHostException, ModbusIOException {
        TcpParameters tcpParameters = new TcpParameters();// 设置从机TCP参数
        InetAddress adress = InetAddress.getByName("127.0.0.1");// 设置TCP的ip地址
        // getLocalHost()返回的是本机地址
        // tcpParameters.setHost(InetAddress.getLocalHost());
        tcpParameters.setHost(adress); // 为从机TCP设置上述ip地址参数
        // 设置从机TCP的是否长连接，通俗点讲就是一直保持连接，一次连接完下次就不要在连接了
        tcpParameters.setKeepAlive(true);
        tcpParameters.setPort(Modbus.TCP_PORT);// 设置从机TCP的端口
        // 创建一个从机
        slave = ModbusSlaveFactory.createModbusSlaveTCP(tcpParameters);
        // 设置控制台输出主机和从机命令交互日志
        Modbus.setLogLevel(Modbus.LogLevel.LEVEL_DEBUG);

    }


    public void createDataHolder() throws ModbusIOException {
        // 创建从机的寄存器
        MyOwnDataHolder dh = new MyOwnDataHolder();

        // 为从机寄存器添加监听事件，这里的监听事件主要是主机如果发送写命令修改从机则控制台输出
        dh.addEventListener(new ModbusEventListener() {
            @Override
            public int[] readHoldingRegisterRange(int offset, int quantity){
                System.out.println("readHoldingRegisterRange: 读取信息 offset = " + offset+";quantity"+quantity);
                try{
                    updateHoldingRegisters(offset, quantity);
                }catch (Exception e){
                    e.printStackTrace();
                }
                return arr;
            }

            @Override
            public int[] readInputRegisterRange(int offset, int quantity){
                System.out.println("readInputRegisterRange: 读取信息 offset = " + offset+";quantity"+quantity);
                try{
                    updateInputRegisters(offset, quantity);
                }catch (Exception e){
                    e.printStackTrace();
                }
                return arr;
            }

            @Override
            public void onWriteToSingleCoil(int address, boolean value) {
                System.out
                        .print("onWriteToSingleCoil: address " + address + ", value " + value);
            }

            @Override
            public void onWriteToMultipleCoils(int address, int quantity, boolean[] values) {
                System.out.print("onWriteToMultipleCoils: address " + address + ", quantity "
                        + quantity);
            }

            @Override
            public void onWriteToSingleHoldingRegister(int address, int value) {
                System.out.print("onWriteToSingleHoldingRegister: address " + address
                        + ", value " + value);
            }

            @Override
            public void onWriteToMultipleHoldingRegisters(int address, int quantity,
                                                          int[] values) {
                System.out.print("onWriteToMultipleHoldingRegisters: address " + address
                        + ", quantity " + quantity);
            }

        });
        // 为从机设置寄存器
        slave.setDataHolder(dh);
        // 设置从机的读超时时间，建议主机读的超时时间小于该值
        slave.setReadTimeout(1500);
        // 为从机设置从机服务地址slaveid
        slave.setServerAddress(1);
        // 开启从机监听事件，必须要这一句
        slave.listen();
    }

    public static void closeSlave() throws InterruptedException, ModbusIOException {
        //这部分代码主要是设置Java虚拟机关闭的时候需要做的事情，即本程序关闭的时候需要做的事情，直接使用即可
        if (slave.isListening()) {
            Runtime.getRuntime().addShutdownHook(new Thread(() -> {
                synchronized (slave) {
                    slave.notifyAll();
                }
            }));

            synchronized (slave) {
                slave.wait();
            }
            slave.shutdown();
            System.out.println("slave shutdown........");
        }
    }

    public void updateHoldingRegisters(int offset, int quantity) throws IllegalDataAddressException, IllegalDataValueException {
        List<Float> list =  Arrays.asList(10.1f,20.3f,89.5f,77.353f);
        ModbusHoldingRegisters hr = new ModbusHoldingRegisters(10000);
        // 修改数值寄存器对应位置的值，第一个参数代表寄存器地址，第二个代表修改的数值
        //hr.set有几个方法，根据自己要赋值的数据类型选择，此处示例的是赋值float类型，一个float是4个字节，32bit,对应2个寄存器所以i*2
        for(int i=0;i<list.size();i++){
            hr.setFloat32At(i*2, list.get(i));
        }
        slave.getDataHolder().setHoldingRegisters(hr);
    }

    public void updateInputRegisters(int offset, int quantity) throws IllegalDataAddressException, IllegalDataValueException, ModbusIOException {
        List<Float> list =  Arrays.asList(10.1f,20.3f,89.5f,77.353f);
        ModbusHoldingRegisters hr = new ModbusHoldingRegisters(10000);
        // 修改数值寄存器对应位置的值，第一个参数代表寄存器地址，第二个代表修改的数值
        for(int i=0;i<list.size();i++){
            hr.setFloat32At(i*2, list.get(i));
        }
        slave.getDataHolder().setInputRegisters(hr);
    }
}
